package com.platform.util.wechat;

import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.platform.entity.WxTradeDetail;
import com.platform.entity.WxTradeSummary;
import com.platform.utils.DateUtil;
import com.platform.utils.HttpUtils;
import com.platform.utils.*;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.util.StringUtils;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.math.MathContext;
import java.net.URLEncoder;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.platform.util.wechat.UrlUtilA.sendPost;

/**
 * <p>Title: 微信退款工具类</p>
 * <p>Description: 微信退款工具类，通过充值客户端的不同初始化不同的工具类，得到相应微信退款相关的appid和muchid</p>
 *
 * @author xubo
 * @date 2017年6月6日  下午5:05:03
 */
public class WechatUtil {
    static {
        // BouncyCastle是一个开源的加解密解决方案
        Security.addProvider(new BouncyCastleProvider());
    }
    private static Log logger = LogFactory.getLog(WechatUtil.class);
    /**
     * 充值客户端类型--微信公众号
     */
    public static Integer CLIENTTYPE_WX = 2;
    /**
     * 充值客户端类型--app
     */
    public static Integer CLIENTTYPE_APP = 1;

    /**
     * 方法描述：微信退款逻辑
     * 创建时间：2017年4月12日  上午11:04:25
     * 作者： xubo
     *
     * @param
     * @return
     */
    public static WechatRefundApiResult wxRefund(String out_trade_no, Double orderMoney, Double refundMoney) {
        //初始化请求微信服务器的配置信息包括appid密钥等
        //转换金钱格式
        BigDecimal bdOrderMoney = new BigDecimal(orderMoney, MathContext.DECIMAL32);
        BigDecimal bdRefundMoney = new BigDecimal(refundMoney, MathContext.DECIMAL32);
        //构建请求参数
        Map<Object, Object> params = buildRequsetMapParam(out_trade_no, bdOrderMoney, bdRefundMoney);
        String mapToXml = MapUtils.convertMap2Xml(params);
        //请求微信
        String reponseXml = sendSSLPostToWx(mapToXml, WechatConfig.getSslcsf());
        WechatRefundApiResult result = (WechatRefundApiResult) XmlUtil.xmlStrToBean(reponseXml, WechatRefundApiResult.class);
        return result;
    }

    /**
     * 方法描述：得到请求微信退款请求的参数
     * 创建时间：2017年6月8日  上午11:27:02
     * 作者： xubo
     *
     * @param
     * @return
     */
    private static Map<Object, Object> buildRequsetMapParam(String out_trade_no, BigDecimal bdOrderMoney, BigDecimal bdRefundMoney) {
        Map<Object, Object> params = new HashMap<Object, Object>();
        //微信分配的公众账号ID（企业号corpid即为此appId）
        params.put("appid", ResourceUtil.getConfigByName("wx.appId"));
        //微信支付分配的商户号
        params.put("mch_id", ResourceUtil.getConfigByName("wx.mchId"));
        //随机字符串，不长于32位。推荐随机数生成算法
        params.put("nonce_str", CharUtil.getRandomString(16));
        //商户侧传给微信的订单号
        params.put("out_trade_no", out_trade_no);
        //商户系统内部的退款单号，商户系统内部唯一，同一退款单号多次请求只退一笔
        params.put("out_refund_no", getBundleId());
        //订单总金额，单位为分，只能为整数
        params.put("total_fee", bdOrderMoney.multiply(new BigDecimal(100)).intValue());
        //退款总金额，订单总金额，单位为分，只能为整数
        params.put("refund_fee", bdRefundMoney.multiply(new BigDecimal(100)).intValue());
        //操作员帐号, 默认为商户号
        params.put("op_user_id", ResourceUtil.getConfigByName("wx.mchId"));
        //签名前必须要参数全部写在前面
        params.put("sign", arraySign(params, ResourceUtil.getConfigByName("wx.paySignKey")));
        return params;
    }


    /**
     * 下载对账单
     */
    public static List<String> downloadFile() throws Exception {
        ArrayList<String> outList = new ArrayList<>();
//        String billDate = "20191217";
        String billDate = DateUtil.getYesterday("yyyyMMdd");
        //签名
        Map<Object, Object> parame = new TreeMap<>();

        parame.put("appid", ResourceUtil.getConfigByName("wx.appId"));                    // 商户号 app_id
        parame.put("mch_id", ResourceUtil.getConfigByName("wx.mchId"));                    // 应用ID mch_id
        parame.put("nonce_str", CharUtil.getRandomString(16));// 随机字符串 nonce_str
        //parame.put("tar_type", "GZIP");//非必传参数，固定值：GZIP，返回格式为.gzip的压缩包账单。不传则默认为数据流形式。
        // ALL（默认值）  SUCCESS，返回当日成功支付的订单（不含充值退款订单） REFUND，返回当日退款订单（不含充值退款订单） RECHARGE_REFUND，返回当日充值退款订单
        parame.put("bill_type", "ALL");
        parame.put("bill_date", billDate);        // 下载对账单的日期，格式：20140603

//        // 签名 sign(MD5)
//        String sign = WeChatAssistantUtils.createSign(WeChatAPIParams.KEY, parameter);
//        // 把数据转为xml
//        String param = WeChatAssistantUtils.parseString2Xml(parameter, sign);
//        String billUrl = WeChatAPIParams.DOWNLOAD_BILL_URL;

        String sign = WechatUtil.arraySign(parame, ResourceUtil.getConfigByName("wx.paySignKey"));

        // 数字签证
        parame.put("sign", sign);

        String xml = MapUtils.convertMap2Xml(parame);
        logger.info("xml:" + xml);
        // 获取下载信息
        String billUrl = ResourceUtil.getConfigByName("wx.downloadBillUrl");
        // 获取下载信息
        String result = HttpUtils.doPost(billUrl, String.valueOf(parame));
        if (StringUtils.hasText(result) && !result.contains("error_code")) {

            // 全部订单的数据
            List<WxTradeDetail> wxTradeDetailList = new ArrayList<WxTradeDetail>();
            // 下载指定日期的所有订单统计
            WxTradeSummary wxTradeSummary = new WxTradeSummary();

            String[] str = result.split("\n");//按行读取数据（*这个尤为重要*）
            int len = str.length;
            for (int i = 0; i < len; i++) {
                String[] tradeDetailArray = str[i].replace("`", "").split(",");
                if (i > 0 && i < (len - 2)) {
                    // 明细行数据[交易时间,公众账号ID,商户号,特约商户号,设备号,微信订单号,商户订单号,用户标识,交易类型,交易状态,付款银行,货币种类,应结订单金额,代金券金额,商品名称,商户数据包,手续费,费率,订单金额,费率备注]
                    WxTradeDetail entity = new WxTradeDetail();
                    entity.setBillDate(billDate);
                    entity.setTransDate(getArrayValue(tradeDetailArray, 0));// 交易时间
                    entity.setCommonId(getArrayValue(tradeDetailArray, 1));// 公众账号ID
                    entity.setBusinessNo(getArrayValue(tradeDetailArray, 2));// 商户号
                    entity.setChildBusinessNo(getArrayValue(tradeDetailArray, 3));// 特约商户号
                    entity.setEquipmentNo(getArrayValue(tradeDetailArray, 4));// 设备号
                    entity.setWxOrderNo(getArrayValue(tradeDetailArray, 5));// 微信订单号
                    entity.setBusinessOrderNo(getArrayValue(tradeDetailArray, 6));// 商户订单号
                    entity.setUserIdentity(getArrayValue(tradeDetailArray, 7));// 用户标识
                    entity.setTransType(getArrayValue(tradeDetailArray, 8));// 交易类型
                    entity.setTransStatus(getArrayValue(tradeDetailArray, 9));// 交易状态
                    entity.setPaymentBank(getArrayValue(tradeDetailArray, 10));// 付款银行
                    entity.setCurrency(getArrayValue(tradeDetailArray, 11)); // 货币种类
                    entity.setSettleAccounts(getArrayValue(tradeDetailArray, 12)); // 应结订单金额
                    entity.setCoupon(getArrayValue(tradeDetailArray, 13)); // 代金券金额
                    entity.setBusinessName(getArrayValue(tradeDetailArray, 14));// 商品名称
                    entity.setBusinessData(getArrayValue(tradeDetailArray, 15));// 商户数据包
                    entity.setFee(getArrayValue(tradeDetailArray, 16));// 手续费
                    entity.setRate(getArrayValue(tradeDetailArray, 17));// 费率
                    entity.setOrderAmount(getArrayValue(tradeDetailArray, 18));// 订单金额
                    entity.setRateRemark(getArrayValue(tradeDetailArray, 19));// 费率备注
                    wxTradeDetailList.add(entity);
                    outList.add(getArrayValue(tradeDetailArray, 6) + "%"
                            + getArrayValue(tradeDetailArray, 7)
                            + getArrayValue(tradeDetailArray, 12)
                    );

                }
                // 汇总行数据 [总交易单数,应结订单总金额,退款总金额,充值券退款总金额,手续费总金额,订单总金额,申请退款总金额]
                if (i > (len - 2)) {
                    wxTradeSummary.setBillDate(billDate);
                    wxTradeSummary.setTradeCount(getArrayValue(tradeDetailArray, 0));// 总交易单数
                    wxTradeSummary.setShouldSettleAmount(getArrayValue(tradeDetailArray, 1));// 应结订单总金额
                    wxTradeSummary.setRefundAmount(getArrayValue(tradeDetailArray, 2));// 退款总金额
                    wxTradeSummary.setRechargeVoucherAmount(getArrayValue(tradeDetailArray, 3));// 充值券退款总金额
                    wxTradeSummary.setServiceCharge(getArrayValue(tradeDetailArray, 4));// 手续费总金额
                    wxTradeSummary.setTotalOrderAmount(getArrayValue(tradeDetailArray, 5));// 订单总金额
                    wxTradeSummary.setApplyRefundAmount(getArrayValue(tradeDetailArray, 6));// 申请退款总金额
                }
            }
        } else {
            System.out.println("###########获取对账数据有误#################");
            System.out.println("返回对账数据为:" + result);
        }
        return outList;
    }


    public static String getArrayValue(String[] tradeDetailArray, int index) {
        try {
            return tradeDetailArray[index];
        } catch (Exception e) {
            return "";
        }
    }

    public static Map<String, List<String>> contrast(List<String> innerOrderList) {
        HashMap<String, List<String>> map = new HashMap<>();
        List<String> successOrderList = new ArrayList<>();
        List<String> errorOrderList = new ArrayList<>();
        List<String> outOrderList = null;
        try {
            outOrderList = downloadFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (outOrderList != null && outOrderList.size() > 0) {
            int num = innerOrderList.size();

            ArrayList<String> list1 = new ArrayList<>();
            ArrayList<String> list2 = new ArrayList<>();
            list1.addAll(innerOrderList);
            list2.addAll(innerOrderList);

            list1.retainAll(outOrderList);
            if (list1.size() > 0) {
                for (String s : list1) {
                    String[] split = s.split("%");
                    successOrderList.add(split[0]);
                }
            }
            list2.removeAll(outOrderList);
            if (list2.size() > 0) {
                for (String s : list2) {
                    String[] split = s.split("%");
                    errorOrderList.add(split[0]);
                }
            }
        }
        map.put("successOrderList", successOrderList);
        map.put("errorOrderList", errorOrderList);
        return map;
    }







    /**
     * 请求微信https
     **/
    public static String sendSSLPostToWx(String mapToXml, SSLConnectionSocketFactory sslcsf) {
        logger.info("*******退款（WX Request：" + mapToXml);
        HttpPost httPost = new HttpPost(ResourceUtil.getConfigByName("wx.refundUrl"));
        httPost.addHeader("Connection", "keep-alive");
        httPost.addHeader("Accept", "*/*");
        httPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        httPost.addHeader("Host", "api.mch.weixin.qq.com");
        httPost.addHeader("X-Requested-With", "XMLHttpRequest");
        httPost.addHeader("Cache-Control", "max-age=0");
        httPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
        httPost.setEntity(new StringEntity(mapToXml, "UTF-8"));
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslcsf).build();
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httPost);
            HttpEntity entity = response.getEntity();
            String xmlStr = EntityUtils.toString(entity, "UTF-8");
            logger.info("*******退款（WX Response：" + xmlStr);
            return xmlStr;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return null;
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 方法描述：微信查询退款逻辑
     * 创建时间：2017年4月12日  上午11:04:25
     * 作者： xubo
     *
     * @param
     * @return
     */
    public Map<String, Object> wxRefundquery(String out_trade_no, String out_refund_no) {
        Map<Object, Object> params = new HashMap<Object, Object>();
        //微信分配的公众账号ID（企业号corpid即为此appId）
        params.put("appid", ResourceUtil.getConfigByName("wx.appId"));
        //微信支付分配的商户号
        params.put("mch_id", ResourceUtil.getConfigByName("wx.mchId"));
        //随机字符串，不长于32位。推荐随机数生成算法
        params.put("nonce_str", CharUtil.getRandomString(16));
        //商户侧传给微信的订单号
        params.put("out_trade_no", out_trade_no);
        //签名前必须要参数全部写在前面
        //签名
        params.put("sign", arraySign(params, ResourceUtil.getConfigByName("wx.paySignKey")));
        String mapToXml = MapUtils.convertMap2Xml(params);
        HttpPost httPost = new HttpPost(ResourceUtil.getConfigByName("wx.refundqueryUrl"));
        httPost.addHeader("Connection", "keep-alive");
        httPost.addHeader("Accept", "*/*");
        httPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        httPost.addHeader("Host", "api.mch.weixin.qq.com");
        httPost.addHeader("X-Requested-With", "XMLHttpRequest");
        httPost.addHeader("Cache-Control", "max-age=0");
        httPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
        httPost.setEntity(new StringEntity(mapToXml, "UTF-8"));
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(WechatConfig.getSslcsf()).build();
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httPost);
            HttpEntity entity = response.getEntity();
            String xmlStr = EntityUtils.toString(entity, "UTF-8");
            System.out.println(xmlStr);
            Map<String, Object> result = XmlUtil.xmlStrToMap(xmlStr);//.xmlStrToBean(xmlStr, WechatRefundApiResult.class);
            return result;
            //将信息保存到数据库
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return null;
        } finally {
            try {
                if (response != null) {
                    response.close();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 支付交易ID
     */
    public static String getBundleId() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String tradeno = dateFormat.format(new Date());
        String str = "000000" + (int) (Math.random() * 1000000);
        tradeno = tradeno + str.substring(str.length() - 6);
        return tradeno;
    }

    /**
     * 方法描述：根据签名加密请求参数
     * 创建时间：2017年6月8日  上午11:28:52
     * 作者： xubo
     *
     * @param
     * @return
     */
    public static String arraySign(Map<Object, Object> params, String paySignKey) {
        boolean encode = false;
        Set<Object> keysSet = params.keySet();
        Object[] keys = keysSet.toArray();
        Arrays.sort(keys);
        StringBuffer temp = new StringBuffer();
        boolean first = true;
        for (Object key : keys) {
            if (first) {
                first = false;
            } else {
                temp.append("&");
            }
            temp.append(key).append("=");
            Object value = params.get(key);
            String valueString = "";
            if (null != value) {
                valueString = value.toString();
            }
            if (encode) {
                try {
                    temp.append(URLEncoder.encode(valueString, "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            } else {
                temp.append(valueString);
            }
        }
        temp.append("&key=");
        temp.append(paySignKey);
        System.out.println(temp.toString());
        String packageSign = MD5.getMessageDigest(temp.toString());
        return packageSign;
    }

    /**
     * 请求，只请求一次，不做重试
     *
     * @param url
     * @param data
     * @return
     * @throws Exception
     */
    public static String requestOnce(final String url, String data) throws Exception {
        BasicHttpClientConnectionManager connManager;
        connManager = new BasicHttpClientConnectionManager(
                RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
                        .register("https", SSLConnectionSocketFactory.getSocketFactory())
                        .build(),
                null,
                null,
                null
        );

        HttpClient httpClient = HttpClientBuilder.create()
                .setConnectionManager(connManager)
                .build();

        HttpPost httpPost = new HttpPost(url);

        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(5000)
                .setConnectTimeout(5000)
                .setConnectionRequestTimeout(10000).build();

        httpPost.setConfig(requestConfig);

        StringEntity postEntity = new StringEntity(data, "UTF-8");
        httpPost.addHeader("Content-Type","text/xml");//"text/xml",
        httpPost.addHeader("User-Agent", "wxpay sdk java v1.0 " + ResourceUtil.getConfigByName("wx.mchId"));
        httpPost.setEntity(postEntity);

        HttpResponse httpResponse = httpClient.execute(httpPost);
        HttpEntity httpEntity = httpResponse.getEntity();
        String reusltObj = EntityUtils.toString(httpEntity, "UTF-8");
        logger.info("请求结果:" + reusltObj);
        return reusltObj;

    }



    public static String decryptPhoneNumber(String sessionKey,String encryptedData, String iv) throws Exception {
            // 被加密的数据
            byte[] dataByte = Base64.decode(encryptedData);
            // 加密秘钥
            byte[] keyByte = Base64.decode(sessionKey);
            // 偏移量
            byte[] ivByte = Base64.decode(iv);
            try {
                // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
                int base = 16;
                if (keyByte.length % base != 0) {
                    int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                    byte[] temp = new byte[groups * base];
                    Arrays.fill(temp, (byte) 0);
                    System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                    keyByte = temp;
                }
                // 初始化
                Security.addProvider(new BouncyCastleProvider());
                Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
                AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
                parameters.init(new IvParameterSpec(ivByte));
                cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
                byte[] resultByte = cipher.doFinal(dataByte);
                if (null != resultByte && resultByte.length > 0) {
                    String result = new String(resultByte, "UTF-8");
                    return result;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
    }

    /**
     * 获取微信小程序 session_key 和 openid
     *
     * @param code 调用微信登陆返回的Code
     * @return
     */
    public static JSONObject getSessionKeyOropenid(String code) {
        //微信端登录code值
        String wxCode = code;
        String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";  //请求地址 https://api.weixin.qq.com/sns/jscode2session
        Map<String, String> requestUrlParam = new HashMap<String, String>();
        requestUrlParam.put("appid", ResourceUtil.getConfigByName("wx.appId"));  //开发者设置中的appId
        requestUrlParam.put("secret", ResourceUtil.getConfigByName("wx.secret")); //开发者设置中的appSecret
        requestUrlParam.put("js_code", wxCode); //小程序调用wx.login返回的code
        requestUrlParam.put("grant_type", "authorization_code");    //默认参数 authorization_code
//        requestUrlParam.put("open")
        //发送post请求读取调用微信 https://api.weixin.qq.com/sns/jscode2session 接口获取openid用户唯一标识
        JSONObject jsonObject = JSON.parseObject(sendPost(requestUrl, requestUrlParam));
        return jsonObject;
    }
}
